﻿using System.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Data;
using System.Windows.Threading;
using CI.Misc;
using CI.Organism;
using CI.PW.Models;

namespace CI.PW.ViewModels
{
    public class StatusControlViewModel : DataModel, IDisposable
    {
        private readonly SimEnv _simEnv = SimEnv.Instance;
        private readonly ObservableCollection<OrganismStatusModel> _aliveOrganismsOC;
        private readonly ObservableCollection<OrganismStatusModel> _deadOrganismsOC;
        private readonly ObservableCollection<OrganismStatusModel> _inactiveOrganismsOC;
        private readonly ICollectionView _aliveOrganismsLCV;
        private readonly ICollectionView _deadOrganismsLCV;
        private readonly ICollectionView _inactiveOrganismsLCV;
        private OrganismStatusModel _currentlySelectedOrganismStatusModel;
        private readonly TimeSpan _refreshRate = new TimeSpan(0, 0, 0, 0, 500);
        private readonly Timer _updateTimer;

        #region bound properties
        public ICollectionView AliveOrganisms
        {
            get { return _aliveOrganismsLCV; }
        }

        public ICollectionView DeadOrganisms
        {
            get { return _deadOrganismsLCV; }
        }

        public ICollectionView InactiveOrganisms
        {
            get { return _inactiveOrganismsLCV; }
        }

        private int _deadCount;
        public int DeadCount
        {
            get { return _deadCount; }
            set
            {
                if (_deadCount != value)
                {
                    _deadCount = value;
                    RaisePropertyChanged("DeadCount");
                }
            }
        }

        private int _inactiveCount;
        public int InactiveCount
        {
            get { return _inactiveCount; }
            set
            {
                if (_inactiveCount != value)
                {
                    _inactiveCount = value;
                    RaisePropertyChanged("InactiveCount");
                }
            }
        }

        private int _aliveCount;
        public int AliveCount
        {
            get { return _aliveCount; }
            set
            {
                if (_aliveCount != value)
                {
                    _aliveCount = value;
                    RaisePropertyChanged("AliveCount");
                }
            }
        }

        private PropertyChangedEventArgs _lagTimePropertyChanged = new PropertyChangedEventArgs("LagTime");
        private TimeSpan _lagTime;
        public TimeSpan LagTime
        {
            get { return _lagTime; }
            set
            {
                if (_lagTime != value)
                {
                    _lagTime = value;
                    RaisePropertyChanged(_lagTimePropertyChanged);
                }
            }
        }

        private PropertyChangedEventArgs _runTimePropertyChanged = new PropertyChangedEventArgs("RunTime");
        public TimeSpan RunTime
        {
            get
            {
                if ((_simEnv.HostEnv == null) || (_emptyDateTime.Equals(_simEnv.HostEnv.StartTime)))
                    return new TimeSpan();
                return DateTime.Now.Subtract(_simEnv.HostEnv.StartTime);
            }
        }
        private DateTime _emptyDateTime;

        private PropertyChangedEventArgs _sysTimePropertyChanged = new PropertyChangedEventArgs("SysTime");
        public TimeSpan SysTime
        {
            get { return DateTime.Now.Subtract(_simEnv.StartTime); }
        }

        private PropertyChangedEventArgs _systemHBPropertyChanged = new PropertyChangedEventArgs("SystemHB");
        private uint _systemHB;
        public uint SystemHB
        {
            get { return _systemHB; }
            set
            {
                if (_systemHB != value)
                {
                    _systemHB = value;
                    RaisePropertyChanged(_systemHBPropertyChanged);
                }
            }
        }

        private PropertyChangedEventArgs _statePropertyChanged = new PropertyChangedEventArgs("State");
        private string _state;
        public string State
        {
            get { return _state; }
            set
            {
                if (_state != value)
                {
                    _state = value;
                    RaisePropertyChanged(_statePropertyChanged);
                }
            }
        }

        private readonly TimeSpan _screenSaverIteratorTime = new TimeSpan(0, 0, 30);

        private bool _simMode = true;
        public bool SimMode
        {
            get { return _simMode; }
            set
            {
                if (_simMode == value)
                    return;

                _simMode = value;
                RaisePropertyChanged("SimMode");
                RaisePropertyChanged("HWMode");

                // if sim remove handlers and clear list
                if (!value)
                {
                    ScenarioEnd();
                    UIDispatcher.BeginInvoke(new Action(delegate
                                                       {
                                                           ClearOrganisms(_aliveOrganismsOC);
                                                           ClearOrganisms(_deadOrganismsOC);
                                                           ClearOrganisms(_inactiveOrganismsOC);
                                                       }));
                }
            }
        }

        #endregion

        public StatusControlViewModel()
        {
            UIDispatcher = Dispatcher.CurrentDispatcher;
            _simEnv.OnSetSelectedOrganismEvent += SetSelectedItem;

            _simEnv.OnMainLoopStartEvent += ScenarioStart;
            _simEnv.OnMainLoopEndEvent += ScenarioEnd;

            _aliveOrganismsOC = new ObservableCollection<OrganismStatusModel>();
            _deadOrganismsOC = new ObservableCollection<OrganismStatusModel>();
            _inactiveOrganismsOC = new ObservableCollection<OrganismStatusModel>();

            _aliveOrganismsLCV = new ListCollectionView(_aliveOrganismsOC);
            _deadOrganismsLCV = new ListCollectionView(_deadOrganismsOC);
            _inactiveOrganismsLCV = new ListCollectionView(_inactiveOrganismsOC);

            _aliveOrganismsLCV.CurrentChanged += OrganismsSelected;
            _deadOrganismsLCV.CurrentChanged += OrganismsSelected;
            _inactiveOrganismsLCV.CurrentChanged += OrganismsSelected;

            // if we started in screen saver mode lets scroll up and down list
            if (Globals.LaunchedFrom == LaunchType.ScreenSaver)
            {
                var screenSaverIterator = new DispatcherTimer { Interval = _screenSaverIteratorTime };
                screenSaverIterator.Tick += ScreensaverNextAlive;
                screenSaverIterator.Start();
            }

            _updateTimer = new Timer(o => ScenarioUpdateDisplay(), null, _refreshRate, _refreshRate);
            _emptyDateTime = new DateTime();

            SetCurrentItemAction = SetCurrentItemHandler;
        }



        private void SetSelectedItem(IOrganism organism)
        {
            if ((_currentlySelectedOrganismStatusModel != null) && (organism == _currentlySelectedOrganismStatusModel.organism))
                return;

            _currentlySelectedOrganismStatusModel = (
                _aliveOrganismsOC.Where(x => x.organism == organism).FirstOrDefault() ??
                _deadOrganismsOC.Where(x => x.organism == organism).FirstOrDefault()) ??
                _inactiveOrganismsOC.Where(x => x.organism == organism).FirstOrDefault();


            UIDispatcher.BeginInvoke(SetCurrentItemAction);
        }

        private readonly Action SetCurrentItemAction;

        private void SetCurrentItemHandler()
        {
            if (_aliveOrganismsLCV.Contains(_currentlySelectedOrganismStatusModel))
            {
                _aliveOrganismsLCV.MoveCurrentTo(_currentlySelectedOrganismStatusModel);
                return;
            }

            if (_deadOrganismsLCV.Contains(_currentlySelectedOrganismStatusModel))
            {
                _deadOrganismsLCV.MoveCurrentTo(_currentlySelectedOrganismStatusModel);
                return;
            }

            if (_inactiveOrganismsLCV.Contains(_currentlySelectedOrganismStatusModel))
            {
                _inactiveOrganismsLCV.MoveCurrentTo(_currentlySelectedOrganismStatusModel);
                return;
            }
        }

        private void ScenarioEnd()
        {
            if (_simEnv.UsingHW)
            {
                SimMode = false;
                return;
            }

            if (_simEnv.HostEnv == null)
                return;

            _simEnv.HostEnv.Organisms.OnAliveAddEvent -= AliveAdd;
            _simEnv.HostEnv.Organisms.OnAliveRemoveEvent -= AliveRemove;
            _simEnv.HostEnv.Organisms.OnDeadAddEvent -= DeadAdd;
            _simEnv.HostEnv.Organisms.OnDeadRemoveEvent -= DeadRemove;
            _simEnv.HostEnv.Organisms.OnInactiveAddEvent -= InactiveAdd;
            _simEnv.HostEnv.Organisms.OnInactiveRemoveEvent -= InactiveRemove;
        }

        private void ScenarioStart()
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    ClearAndAddOrganismModelsToCollection(_simEnv.HostEnv.Organisms.Alive, _aliveOrganismsOC);
                                                    ClearAndAddOrganismModelsToCollection(_simEnv.HostEnv.Organisms.Dead, _deadOrganismsOC);
                                                    ClearAndAddOrganismModelsToCollection(_simEnv.HostEnv.Organisms.Inactive, _inactiveOrganismsOC);

                                                    if (_aliveOrganismsOC.Count > 0)
                                                        _simEnv.SetSelectedOrganism(_aliveOrganismsOC[0].organism);
                                                }));

            if (_simEnv.UsingHW)
            {
                SimMode = false;
                return;
            }

            _simEnv.HostEnv.Organisms.OnAliveAddEvent += AliveAdd;
            _simEnv.HostEnv.Organisms.OnAliveRemoveEvent += AliveRemove;
            _simEnv.HostEnv.Organisms.OnDeadAddEvent += DeadAdd;
            _simEnv.HostEnv.Organisms.OnDeadRemoveEvent += DeadRemove;
            _simEnv.HostEnv.Organisms.OnInactiveAddEvent += InactiveAdd;
            _simEnv.HostEnv.Organisms.OnInactiveRemoveEvent += InactiveRemove;

        }

        private static void ClearAndAddOrganismModelsToCollection(IEnumerable<IOrganism> organismList, ObservableCollection<OrganismStatusModel> organismOC)
        {
            // clean up old ones
            ClearOrganisms(organismOC);

            foreach (var organism in organismList)
            {
                organismOC.Add(new OrganismStatusModel(organism));
            }
        }

        private static void ClearOrganisms(ObservableCollection<OrganismStatusModel> organismOC)
        {
            foreach (var organismModel in organismOC)
            {
                organismModel.Dispose();
            }

            organismOC.Clear();
        }

        private void OrganismsSelected(object sender, EventArgs e)
        {
            if ((sender != null) && (((ListCollectionView)sender).CurrentItem != null))
            {
                if (_currentlySelectedOrganismStatusModel != null)
                    _currentlySelectedOrganismStatusModel.IsSelected = false;

                _currentlySelectedOrganismStatusModel = (OrganismStatusModel)(((ListCollectionView)sender).CurrentItem);
                _simEnv.SetSelectedOrganism(_currentlySelectedOrganismStatusModel.organism);
            }
        }

        #region Observable Collection list updaters
        void InactiveRemove(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    OrganismStatusModel foundOrganism = _inactiveOrganismsOC.FirstOrDefault(x => x.organism == organism);
                                                    _inactiveOrganismsOC.Remove(foundOrganism);
                                                    foundOrganism.Dispose();
                                                    RaisePropertyChanged("InactiveOrganisms");
                                                }));

        }

        void InactiveAdd(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    _inactiveOrganismsOC.Add(new OrganismStatusModel(organism));
                                                    RaisePropertyChanged("InactiveOrganisms");
                                                }));

        }

        void DeadRemove(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    OrganismStatusModel foundOrganism = _deadOrganismsOC.FirstOrDefault(x => x.organism == organism);
                                                    _deadOrganismsOC.Remove(foundOrganism);
                                                    foundOrganism.Dispose();
                                                    RaisePropertyChanged("DeadOrganisms");
                                                }));
        }

        void DeadAdd(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    _deadOrganismsOC.Add(new OrganismStatusModel(organism));
                                                    RaisePropertyChanged("DeadOrganisms");
                                                }));
        }

        void AliveRemove(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    OrganismStatusModel foundOrganism = _aliveOrganismsOC.FirstOrDefault(x => x.organism == organism);
                                                    _aliveOrganismsOC.Remove(foundOrganism);
                                                    foundOrganism.Dispose();
                                                    RaisePropertyChanged("AliveOrganisms");
                                                }));
        }

        void AliveAdd(IOrganism organism)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    _aliveOrganismsOC.Add(new OrganismStatusModel(organism));
                                                    RaisePropertyChanged("AliveOrganisms");
                                                }));
        }

        #endregion

        /// <summary>
        /// Iteratates through the list of items
        /// </summary>
        private void ScreensaverNextAlive(object sender, EventArgs e)
        {
            UIDispatcher.BeginInvoke(new Action(delegate
                                                {
                                                    if (_aliveOrganismsLCV != null)
                                                    {
                                                        _aliveOrganismsLCV.MoveCurrentToNext();
                                                        if (_aliveOrganismsLCV.IsCurrentAfterLast)
                                                            _aliveOrganismsLCV.MoveCurrentToFirst();
                                                    }
                                                }));
        }

        private void ScenarioUpdateDisplay()
        {
            RaisePropertyChanged(_sysTimePropertyChanged);

            if (_simEnv.HostEnv == null) return;

            // also update the run times etc
            RaisePropertyChanged(_runTimePropertyChanged);

            if (_aliveOrganismsOC != null)
                AliveCount = _aliveOrganismsOC.Count;
            if (_deadOrganismsOC != null)
                DeadCount = _deadOrganismsOC.Count;
            if (_inactiveOrganismsOC != null)
                InactiveCount = _inactiveOrganismsOC.Count;

            LagTime = _simEnv.HostEnv.LagTime;
            SystemHB = _simEnv.HostEnv.SystemHeartBeat;
            State = _simEnv.HostEnv.ProcessingState;
        }

        public void Dispose()
        {
            ScenarioEnd();
            if (_updateTimer != null)
                _updateTimer.Dispose();
        }
    }
}